/******************************************************************************
 *
 *
 *
 * Copyright (C) 1997-2015 by Parker Waechter & Dimitri van Heesch.
 *
 * Style sheet additions by Alexander Bartolich
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation under the terms of the GNU General Public License is hereby
 * granted. No representations are made about the suitability of this software
 * for any purpose. It is provided "as is" without express or implied warranty.
 * See the GNU General Public License for more details.
 *
 * Documents produced by Doxygen are derivative works derived from the
 * input used in their production; they are not affected by this license.
 *
 */

#include <chrono>
#include <ctime>
#include <stdlib.h>

#include "rtfgen.h"
#include "config.h"
#include "message.h"
#include "doxygen.h"
#include "util.h"
#include "diagram.h"
#include "language.h"
#include "dot.h"
#include "dotcallgraph.h"
#include "dotclassgraph.h"
#include "dotdirdeps.h"
#include "dotincldepgraph.h"
#include "version.h"
#include "pagedef.h"
#include "rtfstyle.h"
#include "rtfdocvisitor.h"
#include "docparser.h"
#include "dirdef.h"
#include "vhdldocgen.h"
#include "portable.h"
#include "groupdef.h"
#include "classlist.h"
#include "filename.h"
#include "namespacedef.h"
#include "dir.h"


//#define DBG_RTF(x) x;
#define DBG_RTF(x)

static QCString dateToRTFDateString()
{
  auto now = std::chrono::system_clock::now();
  auto time = std::chrono::system_clock::to_time_t(now);
  auto tm = *localtime(&time);

  QCString result;
  result.sprintf("\\yr%d\\mo%d\\dy%d\\hr%d\\min%d\\sec%d",
      tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
      tm.tm_hour, tm.tm_min, tm.tm_sec);
  return result;
}

RTFGenerator::RTFGenerator() : OutputGenerator(Config_getString(RTF_OUTPUT))
{
}

RTFGenerator::RTFGenerator(const RTFGenerator &og) : OutputGenerator(og)
{
}

RTFGenerator &RTFGenerator::operator=(const RTFGenerator &og)
{
  OutputGenerator::operator=(og);
  return *this;
}

std::unique_ptr<OutputGenerator> RTFGenerator::clone() const
{
  return std::make_unique<RTFGenerator>(*this);
}

RTFGenerator::~RTFGenerator()
{
}

void RTFGenerator::setRelativePath(const QCString &path)
{
  m_relPath = path;
}

void RTFGenerator::setSourceFileName(const QCString &name)
{
  m_sourceFileName = name;
}

void RTFGenerator::writeStyleSheetFile(std::ostream &t)
{
  t << "# Generated by doxygen " << getDoxygenVersion() << "\n\n";
  t << "# This file describes styles used for generating RTF output.\n";
  t << "# All text after a hash (#) is considered a comment and will be ignored.\n";
  t << "# Remove a hash to activate a line.\n\n";

  int i;
  for ( i=0 ; rtf_Style_Default[i].reference!=0 ; i++ )
  {
    t << "# " << rtf_Style_Default[i].name << " = "
              << rtf_Style_Default[i].reference
              << rtf_Style_Default[i].definition << "\n";
  }
}

void RTFGenerator::writeExtensionsFile(std::ostream &t)
{
  t << "# Generated by doxygen " << getDoxygenVersion() << "\n\n";
  t << "# This file describes extensions used for generating RTF output.\n";
  t << "# All text after a hash (#) is considered a comment and will be ignored.\n";
  t << "# Remove a hash to activate a line.\n\n";

  t << "# Overrides the project title.\n";

  t << "#Title           = \n\n";

  t << "# Name of the company that produced this document.\n";
  t << "#Company         = \n\n";

  t << "# Filename of a company or project logo.\n";
  t << "#LogoFilename    = \n\n";

  t << "# Author of the document.\n";
  t << "#Author          = \n\n";

  t << "# Type of document (e.g. Design Specification, User Manual, etc.).\n";
  t << "#DocumentType    = \n\n";

  t << "# Document tracking number.\n";
  t << "#DocumentId      = \n\n";

  t << "# Name of the author's manager.\n";
  t << "# This field is not displayed in the document itself, but it is \n";
  t << "# available in the information block of the rtf file.  In Microsoft \n";
  t << "# Word, it is available under File:Properties.\n";
  t << "#Manager         = \n\n";

  t << "# Subject of the document.\n";
  t << "# This field is not displayed in the document itself, but it is \n";
  t << "# available in the information block of the rtf file.  In Microsoft \n";
  t << "# Word, it is available under File:Properties.\n";
  t << "#Subject         = \n\n";

  t << "# Comments regarding the document.\n";
  t << "# This field is not displayed in the document itself, but it is \n";
  t << "# available in the information block of the rtf file.  In Microsoft \n";
  t << "# Word, it is available under File:Properties.\n";
  t << "#Comments        = \n\n";

  t << "# Keywords associated with the document.\n";
  t << "# This field is not displayed in the document itself, but it is \n";
  t << "# available in the information block of the rtf file.  In Microsoft \n";
  t << "# Word, it is available under File:Properties.\n";
  t << "#Keywords        = \n\n";
}


void RTFGenerator::init()
{
  QCString dir=Config_getString(RTF_OUTPUT);
  Dir d(dir.str());
  if (!d.exists() && !d.mkdir(dir.str()))
  {
    term("Could not create output directory %s\n",dir.data());
  }

  // first duplicate strings of rtf_Style_Default
  const struct Rtf_Style_Default* def = rtf_Style_Default;
  while (def->reference)
  {
    if (def->definition == 0)
    {
      err("Internal: rtf_Style_Default[%s] has no definition.\n", def->name);
    }
    else
    {
      rtf_Style.insert(std::make_pair(def->name, StyleData(def->reference, def->definition)));
    }
    def++;
  }

  // overwrite some (or all) definitions from file
  QCString rtfStyleSheetFile = Config_getString(RTF_STYLESHEET_FILE);
  if (!rtfStyleSheetFile.isEmpty())
  {
    loadStylesheet(rtfStyleSheetFile, rtf_Style);
  }

  // If user has defined an extension file, load its contents.
  QCString rtfExtensionsFile = Config_getString(RTF_EXTENSIONS_FILE);
  if (!rtfExtensionsFile.isEmpty())
  {
    loadExtensions(rtfExtensionsFile);
  }

  createSubDirs(d);
}

static QCString makeIndexName(const char *s,int i)
{
  QCString result=s;
  result+=(char)(i+'0');
  return result;
}

void RTFGenerator::beginRTFDocument()
{
  /* all the included RTF files should begin with the
   * same header
   */
  t <<"{\\rtf1\\ansi\\ansicpg" << theTranslator->trRTFansicp();
  t <<"\\uc1 \\deff0\\deflang1033\\deflangfe1033\n";

  DBG_RTF(t <<"{\\comment Beginning font list}\n")
  t <<"{\\fonttbl ";
  t <<"{\\f0\\froman\\fcharset" << theTranslator->trRTFCharSet();
  t <<"\\fprq2{\\*\\panose 02020603050405020304}Times New Roman;}\n";
  t <<"{\\f1\\fswiss\\fcharset" << theTranslator->trRTFCharSet();
  t <<"\\fprq2{\\*\\panose 020b0604020202020204}Arial;}\n";
  t <<"{\\f2\\fmodern\\fcharset" << theTranslator->trRTFCharSet();
  t <<"\\fprq1{\\*\\panose 02070309020205020404}Courier New;}\n";
  t <<"{\\f3\\froman\\fcharset2\\fprq2{\\*\\panose 05050102010706020507}Symbol;}\n";
  t <<"}\n";
  DBG_RTF(t <<"{\\comment begin colors}\n")
  t <<"{\\colortbl;";
  t <<"\\red0\\green0\\blue0;";
  t <<"\\red0\\green0\\blue255;";
  t <<"\\red0\\green255\\blue255;";
  t <<"\\red0\\green255\\blue0;";
  t <<"\\red255\\green0\\blue255;";
  t <<"\\red255\\green0\\blue0;";
  t <<"\\red255\\green255\\blue0;";
  t <<"\\red255\\green255\\blue255;";
  t <<"\\red0\\green0\\blue128;";
  t <<"\\red0\\green128\\blue128;";
  t <<"\\red0\\green128\\blue0;";
  t <<"\\red128\\green0\\blue128;";
  t <<"\\red128\\green0\\blue0;";
  t <<"\\red128\\green128\\blue0;";
  t <<"\\red128\\green128\\blue128;";
  t <<"\\red192\\green192\\blue192;";

  // code highlighting colors. Note order is important see also RTFGenerator::startFontClass
  t <<"\\red0\\green128\\blue0;";   // keyword = index 17
  t <<"\\red96\\green64\\blue32;";  // keywordtype
  t <<"\\rede0\\green128\\blue0;";  // keywordflow
  t <<"\\red128\\green0\\blue0;";   // comment
  t <<"\\red128\\green96\\blue32;"; // preprocessor
  t <<"\\red0\\green32\\blue128;";  // stringliteral
  t <<"\\red0\\green128\\blue128;"; // charliteral
  t <<"\\red255\\green0\\blue255;"; // vhdldigit
  t <<"\\red0\\green0\\blue0;";     // vhdlchar
  t <<"\\red112\\green0\\blue112;"; // vhdlkeyword
  t <<"\\red255\\green0\\blue0;";   // vhdllogic

  t <<"}\n";

  DBG_RTF(t <<"{\\comment Beginning style list}\n")
  t <<"{\\stylesheet\n";
  t <<"{\\widctlpar\\adjustright \\fs20\\cgrid \\snext0 Normal;}\n";

  // set the paper dimensions according to PAPER_TYPE
  QCString paperName = Config_getEnum(PAPER_TYPE);
  t << "{";
  if (paperName=="a4")
  {
    t << "\\paperw11900\\paperh16840"; // width & height values are inches * 1440
  }
  else if (paperName=="letter")
  {
    t << "\\paperw12240\\paperh15840";
  }
  else if (paperName=="legal")
  {
    t << "\\paperw12240\\paperh20160";
  }
  else if (paperName=="executive")
  {
    t << "\\paperw10440\\paperh15120";
  }
  t << "\\margl1800\\margr1800\\margt1440\\margb1440\\gutter0\\ltrsect}\n";

  // sort styles ascending by \s-number via an intermediate QArray
  unsigned maxIndex = 0;
  for (const auto &kv : rtf_Style)
  {
    uint index = kv.second.index();
    if (index > maxIndex) maxIndex = index;
  }
  std::vector<const StyleData*> array(maxIndex + 1, 0);
  ASSERT(maxIndex < array.size());

  for (const auto &kv : rtf_Style)
  {
    uint index = kv.second.index();
    if (array[index] != 0)
    {
      msg("Style '%s' redefines \\s%d.\n", kv.first.c_str(), index);
    }
    array[index] = &kv.second;
  }

  // write array elements
  size_t size = array.size();
  for(size_t i = 0; i < size; i++)
  {
    const StyleData *pStyle = array[i];
    if (pStyle)
    {
      t <<"{" << pStyle->reference() << pStyle->definition() << ";}\n";
    }
  }

  t <<"}\n";
  // this comment is needed for postprocessing!
  t <<"{\\comment begin body}\n";

}

void RTFGenerator::beginRTFChapter()
{
  t <<"\n";
  DBG_RTF(t << "{\\comment BeginRTFChapter}\n")
  t << rtf_Style_Reset;

  // if we are compact, no extra page breaks...
  if (Config_getBool(COMPACT_RTF))
  {
    //      t <<"\\sect\\sectd\\sbknone\n";
    t <<"\\sect\\sbknone\n";
    rtfwriteRuler_thick();
  }
  else
    t <<"\\sect\\sbkpage\n";
  //t <<"\\sect\\sectd\\sbkpage\n";

  t << rtf_Style["Heading1"].reference() << "\n";
}

void RTFGenerator::beginRTFSection()
{
  t <<"\n";
  DBG_RTF(t << "{\\comment BeginRTFSection}\n")
  t << rtf_Style_Reset;

  // if we are compact, no extra page breaks...
  if (Config_getBool(COMPACT_RTF))
  {
    t <<"\\sect\\sbknone\n";
    rtfwriteRuler_emboss();
  }
  else
  {
    t <<"\\sect\\sbkpage\n";
  }

  t << rtf_Style["Heading2"].reference() << "\n";
}

void RTFGenerator::startFile(const char *name,const char *,const char *,int)
{
  //setEncoding(QCString().sprintf("CP%s",theTranslator->trRTFansicp()));
  QCString fileName=name;
  m_relPath = relativePathToRoot(fileName);

  if (fileName.right(4)!=".rtf" ) fileName+=".rtf";
  startPlainFile(fileName);
  setRelativePath(m_relPath);
  setSourceFileName(stripPath(fileName));
  beginRTFDocument();
}

void RTFGenerator::endFile()
{
  DBG_RTF(t << "{\\comment endFile}\n")
  t << "}";

  endPlainFile();
  setSourceFileName("");
}

void RTFGenerator::startProjectNumber()
{
  DBG_RTF(t <<"{\\comment startProjectNumber }\n")
  t << " ";
}

void RTFGenerator::endProjectNumber()
{
  DBG_RTF(t <<"{\\comment endProjectNumber }\n")
}

void RTFGenerator::startIndexSection(IndexSections is)
{
  //QCString paperName;

  m_listLevel = 0;

  switch (is)
  {
    case isTitlePageStart:
      // basic RTFstart
      // get readyfor author etc

      t << "{\\info \n";
      t << "{\\title {\\comment ";
      break;
    case isTitlePageAuthor:
      t << "}\n";
      if (rtf_subject)      t << "{\\subject "  << rtf_subject      << "}\n";
      if (rtf_comments)     t << "{\\comment "  << rtf_comments     << "}\n";
      if (rtf_company)      t << "{\\company "  << rtf_company      << "}\n";
      if (rtf_author)       t << "{\\author "   << rtf_author       << "}\n";
      if (rtf_manager)      t << "{\\manager "  << rtf_manager      << "}\n";
      if (rtf_documentType) t << "{\\category " << rtf_documentType << "}\n";
      if (rtf_keywords)     t << "{\\keywords " << rtf_keywords     << "}\n";
      t << "{\\comment ";
      break;
    case isMainPage:
      //Introduction
      beginRTFChapter();
      break;
    //case isPackageIndex:
    //  //Package Index
    //  beginRTFChapter();
    //  break;
    case isModuleIndex:
      //Module Index
      beginRTFChapter();
      break;
    case isDirIndex:
      //Directory Index
      beginRTFChapter();
      break;
    case isNamespaceIndex:
      //Namespace Index
      beginRTFChapter();
      break;
    case isClassHierarchyIndex:
      //Hierarchical Index
      DBG_RTF(t << "{\\comment start classhierarchy}\n")
      beginRTFChapter();
      break;
    case isCompoundIndex:
      //Annotated Compound Index
      beginRTFChapter();
      break;
    case isFileIndex:
      //Annotated File Index
      beginRTFChapter();
      break;
    case isPageIndex:
      //Related Page Index
      beginRTFChapter();
      break;
    case isModuleDocumentation:
      {
        //Module Documentation
        for (const auto &gd : *Doxygen::groupLinkedMap)
        {
          if (!gd->isReference())
          {
            beginRTFChapter();
            break;
          }
        }
      }
      break;
    case isDirDocumentation:
      {
        //Directory Documentation
        for (const auto &dd : *Doxygen::dirLinkedMap)
        {
          if (dd->isLinkableInProject())
          {
            beginRTFChapter();
            break;
          }
        }
      }
      break;
    case isNamespaceDocumentation:
      {
        // Namespace Documentation
        for (const auto &nd : *Doxygen::namespaceLinkedMap)
        {
          if (nd->isLinkableInProject())
          {
            beginRTFChapter();
            break;
          }
        }
      }
      break;
    case isClassDocumentation:
      {
        //Compound Documentation
        for (const auto &cd : *Doxygen::classLinkedMap)
        {
          if (cd->isLinkableInProject() &&
              cd->templateMaster()==0 &&
             !cd->isEmbeddedInOuterScope() &&
             !cd->isAlias()
             )
          {
            beginRTFChapter();
            break;
          }
        }
      }
      break;
    case isFileDocumentation:
      {
        //File Documentation
        bool isFirst=TRUE;
        for (const auto &fn : *Doxygen::inputNameLinkedMap)
        {
          for (const auto &fd : *fn)
          {
            if (fd->isLinkableInProject())
            {
              if (isFirst)
              {
                beginRTFChapter();
                isFirst=FALSE;
                break;
              }
            }
          }
          if (!isFirst)
          {
            break;
          }
        }
      }
      break;
    case isExampleDocumentation:
      {
        //Example Documentation
        beginRTFChapter();
      }
      break;
    case isPageDocumentation:
      {
        //Page Documentation
        beginRTFChapter();
      }
      break;
    case isPageDocumentation2:
      {
        t << "{\\tc \\v ";
      }
      break;
    case isEndIndex:
      break;
  }
}

void RTFGenerator::endIndexSection(IndexSections is)
{
  bool fortranOpt = Config_getBool(OPTIMIZE_FOR_FORTRAN);
  bool vhdlOpt    = Config_getBool(OPTIMIZE_OUTPUT_VHDL);
  bool sourceBrowser = Config_getBool(SOURCE_BROWSER);
  QCString projectName = Config_getString(PROJECT_NAME);

  switch (is)
  {
    case isTitlePageStart:
      if (rtf_title)
        // User has overridden document title in extensions file
        t << "}" << rtf_title;
      else
        t << "}" << projectName;
      break;
    case isTitlePageAuthor:
      {
        t << " doxygen" << getDoxygenVersion() << ".}\n";
        t << "{\\creatim " << dateToRTFDateString() << "}\n}";
        DBG_RTF(t << "{\\comment end of infoblock}\n");
        // setup for this section
        t << rtf_Style_Reset <<"\n";
        t <<"\\sectd\\pgnlcrm\n";
        t <<"{\\footer "<<rtf_Style["Footer"].reference() << "{\\chpgn}}\n";
        // the title entry
        DBG_RTF(t << "{\\comment begin title page}\n")


        t << rtf_Style_Reset << rtf_Style["SubTitle"].reference() << "\n"; // set to title style

        t << "\\vertalc\\qc\\par\\par\\par\\par\\par\\par\\par\n";
        if (rtf_logoFilename)
        {
          t << "{\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \"" << rtf_logoFilename;
          t << "\" \\\\d \\\\*MERGEFORMAT} {\\fldrslt IMAGE }}\\par\\par\n";
        }
        if (rtf_company)
        {
          t << rtf_company << "\\par\\par\n";
        }

        t << rtf_Style_Reset << rtf_Style["Title"].reference() << "\n"; // set to title style
        if (rtf_title)
          // User has overridden document title in extensions file
          t << "{\\field\\fldedit {\\*\\fldinst TITLE \\\\*MERGEFORMAT}{\\fldrslt " << rtf_title << "}}\\par\n";
        else
        {
          DocText *root = validatingParseText(projectName);
          t << "{\\field\\fldedit {\\*\\fldinst TITLE \\\\*MERGEFORMAT}{\\fldrslt ";
          writeDoc(root,0,0,0);
          t << "}}\\par\n";

        }

        t << rtf_Style_Reset << rtf_Style["SubTitle"].reference() << "\n"; // set to title style
        t << "\\par\n";
        if (rtf_documentType)
        {
          t << rtf_documentType << "\\par\n";
        }
        if (rtf_documentId)
        {
          t << rtf_documentId << "\\par\n";
        }
        t << "\\par\\par\\par\\par\\par\\par\\par\\par\\par\\par\\par\\par\n";

        t << rtf_Style_Reset << rtf_Style["SubTitle"].reference() << "\n"; // set to subtitle style
        if (rtf_author)
          t << "{\\field\\fldedit {\\*\\fldinst AUTHOR \\\\*MERGEFORMAT}{\\fldrslt "<< rtf_author << " }}\\par\n";
        else
          t << "{\\field\\fldedit {\\*\\fldinst AUTHOR \\\\*MERGEFORMAT}{\\fldrslt AUTHOR}}\\par\n";

        t << theTranslator->trVersion() << " " << Config_getString(PROJECT_NUMBER) << "\\par";
        t << "{\\field\\fldedit {\\*\\fldinst CREATEDATE \\\\*MERGEFORMAT}"
          "{\\fldrslt "<< dateToString(FALSE) << " }}\\par\n";
        t << "\\page\\page";
        DBG_RTF(t << "{\\comment End title page}\n")

        // table of contents section
        DBG_RTF(t << "{\\comment Table of contents}\n")
        t << "\\vertalt\n";
        t << rtf_Style_Reset << "\n";
        t << rtf_Style["Heading1"].reference();
        t << theTranslator->trRTFTableOfContents() << "\\par\n";
        t << rtf_Style_Reset << "\\par\n";
        t << "{\\field\\fldedit {\\*\\fldinst TOC \\\\f \\\\*MERGEFORMAT}{\\fldrslt Table of contents}}\\par\n";
        t << rtf_Style_Reset << "\n";
      }
      break;
    case isMainPage:
      t << "\\par " << rtf_Style_Reset << "\n";
      if (!mainPageHasTitle())
      {
        t << "{\\tc \\v " << theTranslator->trMainPage() << "}\n";
      }
      else
      {
        t << "{\\tc \\v " << substitute(Doxygen::mainPage->title(),"%","") << "}\n";
      }
      t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
      //if (Config_getBool(GENERATE_TREEVIEW)) t << "main"; else t << "index";
      t << "index";
      t << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
      break;
    //case isPackageIndex:
    //  t << "\\par " << rtf_Style_Reset << "\n";
    //  t << "{\\tc \\v " << theTranslator->trPackageList() << "}\n";
    //  t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"packages.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
    //  break;
    case isModuleIndex:
      t << "\\par " << rtf_Style_Reset << "\n";
      t << "{\\tc \\v " << theTranslator->trModuleIndex() << "}\n";
      t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"modules.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
      break;
    case isDirIndex:
      t << "\\par " << rtf_Style_Reset << "\n";
      t << "{\\tc \\v " << theTranslator->trDirIndex() << "}\n";
      t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"dirs.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
      break;
    case isNamespaceIndex:
      t << "\\par " << rtf_Style_Reset << "\n";
      if (fortranOpt)
      {
          t << "{\\tc \\v " << theTranslator->trModulesIndex() << "}\n";
      }
      else
      {
          t << "{\\tc \\v " << theTranslator->trNamespaceIndex() << "}\n";
      }

      t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"namespaces.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
      break;
    case isClassHierarchyIndex:
      t << "\\par " << rtf_Style_Reset << "\n";
      t << "{\\tc \\v " << theTranslator->trHierarchicalIndex() << "}\n";
      t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"hierarchy.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
      break;
    case isCompoundIndex:
      t << "\\par " << rtf_Style_Reset << "\n";
      if (fortranOpt)
      {
        t << "{\\tc \\v " << theTranslator->trCompoundIndexFortran() << "}\n";
      }
      else if (vhdlOpt)
      {
        t << "{\\tc \\v " << theTranslator->trDesignUnitIndex() << "}\n";
      }
      else
      {
        t << "{\\tc \\v " << theTranslator->trCompoundIndex() << "}\n";
      }
      t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"annotated.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
      break;
    case isFileIndex:
      t << "\\par " << rtf_Style_Reset << "\n";
      t << "{\\tc \\v " << theTranslator->trFileIndex() << "}\n";
      t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"files.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
      break;
    case isPageIndex:
      t << "\\par " << rtf_Style_Reset << "\n";
      t << "{\\tc \\v " << theTranslator->trPageIndex() << "}\n";
      t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"pages.rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
      break;
    case isModuleDocumentation:
      {
        bool first=true;
        t << "{\\tc \\v " << theTranslator->trModuleDocumentation() << "}\n";
        for (const auto &gd : *Doxygen::groupLinkedMap)
        {
          if (!gd->isReference())
          {
            t << "\\par " << rtf_Style_Reset << "\n";
            if (!first)
            {
              beginRTFSection();
            }
            first=false;
            t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
            t << gd->getOutputFileBase();
            t << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
          }
        }
      }
      break;
    case isDirDocumentation:
      {
        bool first=true;
        t << "{\\tc \\v " << theTranslator->trDirDocumentation() << "}\n";
        for (const auto &dd : *Doxygen::dirLinkedMap)
        {
          if (dd->isLinkableInProject())
          {
            t << "\\par " << rtf_Style_Reset << "\n";
            if (!first)
            {
              beginRTFSection();
            }
            first=false;
            t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
            t << dd->getOutputFileBase();
            t << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
          }
        }
      }
      break;
    case isNamespaceDocumentation:
      {
        bool first=true;
        for (const auto &nd : *Doxygen::namespaceLinkedMap)
        {
          if (nd->isLinkableInProject() && !nd->isAlias())
          {
            t << "\\par " << rtf_Style_Reset << "\n";
            if (!first)
            {
              beginRTFSection();
            }
            first=false;
            t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
            t << nd->getOutputFileBase();
            t << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
          }
        }
      }
      break;
    case isClassDocumentation:
      {
        bool first=true;
        if (fortranOpt)
        {
          t << "{\\tc \\v " << theTranslator->trTypeDocumentation() << "}\n";
        }
        else
        {
          t << "{\\tc \\v " << theTranslator->trClassDocumentation() << "}\n";
        }
        for (const auto &cd : *Doxygen::classLinkedMap)
        {
          if (cd->isLinkableInProject() &&
              cd->templateMaster()==0 &&
             !cd->isEmbeddedInOuterScope() &&
             !cd->isAlias()
             )
          {
            t << "\\par " << rtf_Style_Reset << "\n";
            if (!first)
            {
              beginRTFSection();
            }
            first=false;
            t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
            t << cd->getOutputFileBase();
            t << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
          }
        }
      }
      break;
    case isFileDocumentation:
      {
        bool isFirst=TRUE;

        t << "{\\tc \\v " << theTranslator->trFileDocumentation() << "}\n";
        for (const auto &fn : *Doxygen::inputNameLinkedMap)
        {
          for (const auto &fd : *fn)
          {
            if (fd->isLinkableInProject())
            {
              t << "\\par " << rtf_Style_Reset << "\n";
              if (!isFirst)
              {
                beginRTFSection();
              }
              isFirst=FALSE;
              t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
              t << fd->getOutputFileBase();
              t << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
              if (sourceBrowser && m_prettyCode && fd->generateSourceFile())
              {
                t << "\\par " << rtf_Style_Reset << "\n";
                t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"" << fd->getSourceFileBase() << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
              }
            }
          }
        }
      }
      break;
    case isExampleDocumentation:
      {
        //t << "}\n";
        bool isFirst=true;
        t << "{\\tc \\v " << theTranslator->trExampleDocumentation() << "}\n";
        for (const auto &pd : *Doxygen::exampleLinkedMap)
        {
          t << "\\par " << rtf_Style_Reset << "\n";
          if (!isFirst)
          {
            beginRTFSection();
          }
          isFirst=false;
          t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
          t << pd->getOutputFileBase();
          t << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
        }
      }
      break;
    case isPageDocumentation:
      {
//#error "fix me in the same way as the latex index..."
        //t << "{\\tc \\v " << theTranslator->trPageDocumentation() << "}\n";
        //t << "}\n";
        //bool first=TRUE;
        //for (const auto *pd : Doxygen::pageLinkedMap)
        //{
        //  if (!pd->getGroupDef() && !pd->isReference())
        //  {
        //    if (first) t << "\\par " << rtf_Style_Reset << "\n";
        //    t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
        //    t << pd->getOutputFileBase();
        //    t << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
        //    first=FALSE;
        //  }
        //}
      }
      break;
    case isPageDocumentation2:
      {
        t << "}";
        t << "\\par " << rtf_Style_Reset << "\n";
      }
      break;
    case isEndIndex:
      beginRTFChapter();
      t << rtf_Style["Heading1"].reference();
      t << theTranslator->trRTFGeneralIndex() << "\\par \n";
      t << rtf_Style_Reset << "\n";
      t << "{\\tc \\v " << theTranslator->trRTFGeneralIndex() << "}\n";
      t << "{\\field\\fldedit {\\*\\fldinst INDEX \\\\c2 \\\\*MERGEFORMAT}{\\fldrslt INDEX}}\n";

      break;
   }
}

void RTFGenerator::writePageLink(const char *name,bool first)
{
   if (first) t << "\\par " << rtf_Style_Reset << "\n";
   t << "{\\field\\fldedit{\\*\\fldinst INCLUDETEXT \"";
   t << name;
   t << ".rtf\" \\\\*MERGEFORMAT}{\\fldrslt includedstuff}}\n";
}

void RTFGenerator::lastIndexPage()
{
  DBG_RTF(t <<"{\\comment Beginning Body of RTF Document}\n")
  // end page and setup for rest of document
  t <<"\\sect \\sbkpage \\pgndec \\pgnrestart\n";
  t <<"\\sect \\sectd \\sbknone\n";

  // set new footer with arabic numbers
  t <<"{\\footer "<< rtf_Style["Footer"].reference() << "{\\chpgn}}\n";

}

void RTFGenerator::writeStyleInfo(int)
{
}

void RTFGenerator::lineBreak(const char *)
{
  DBG_RTF(t << "{\\comment (lineBreak)}"    << "\n")
  t << "\\par\n";
  m_omitParagraph = TRUE;
}

void RTFGenerator::writeString(const char *text)
{
  t << text;
}

void RTFGenerator::startIndexList()
{
  DBG_RTF(t << "{\\comment (startIndexList)}\n")
  t << "{\n";
  t << "\\par\n";
  incrementIndentLevel();
  t << rtf_Style_Reset << rtf_LCList_DepthStyle() << "\n";
  m_omitParagraph = TRUE;
}

void RTFGenerator::endIndexList()
{
  DBG_RTF(t << "{\\comment (endIndexList)}\n")
  if (!m_omitParagraph)
  {
    t << "\\par";
    m_omitParagraph = TRUE;
  }
  t << "}";
  decrementIndentLevel();
}

/*! start bullet list */
void RTFGenerator::startItemList()
{
  newParagraph();
  DBG_RTF(t << "{\\comment (startItemList level=" << m_listLevel << ") }\n")
  t << "{";
  incrementIndentLevel();
  rtf_listItemInfo[m_listLevel].isEnum = FALSE;
}

/*! end bullet list */
void RTFGenerator::endItemList()
{
  newParagraph();
  DBG_RTF(t << "{\\comment (endItemList level=" << m_listLevel << ")}\n")
  t << "}";
  decrementIndentLevel();
  m_omitParagraph = TRUE;
}

///*! start enumeration list */
//void RTFGenerator::startEnumList()  // starts an enumeration list
//{
//  DBG_RTF(t << "{\\comment (startEnumList)}\n")
//  t << "{\n";
//  incrementIndentLevel();
//  rtf_listItemInfo[m_listLevel].isEnum = TRUE;
//  rtf_listItemInfo[m_listLevel].number = 1;
//}
//
///*! end enumeration list */
//void RTFGenerator::endEnumList()
//{
//  newParagraph();
//  DBG_RTF(t << "{\\comment (endEnumList)}\n")
//  t << "}";
//  decrementIndentLevel();
//  m_omitParagraph = TRUE;
//}

/*! write bullet or enum item */
void RTFGenerator::startItemListItem()
{
  DBG_RTF(t << "{\\comment (startItemListItem)}\n")
  newParagraph();
  t << rtf_Style_Reset;
  if (rtf_listItemInfo[m_listLevel].isEnum)
  {
    t << rtf_EList_DepthStyle() << "\n";
    t << rtf_listItemInfo[m_listLevel].number << ".\\tab ";
    rtf_listItemInfo[m_listLevel].number++;
  }
  else
  {
    t << rtf_BList_DepthStyle() << "\n";
  }
  m_omitParagraph = TRUE;
}

void RTFGenerator::endItemListItem()
{
  DBG_RTF(t << "{\\comment (endItemListItem)}\n")
}

void RTFGenerator::startIndexItem(const char *,const char *)
{
  DBG_RTF(t << "{\\comment (startIndexItem)}\n")

  if (!m_omitParagraph)
  {
    t << "\\par\n";
    m_omitParagraph = TRUE;
  }
}

void RTFGenerator::endIndexItem(const char *ref,const char *fn)
{
  DBG_RTF(t << "{\\comment (endIndexItem)}\n")
  if (!ref && fn)
  {
    t << "\\tab ";
    writeRTFReference(fn);
    t << "\n";
  }
  else
  {
    t << "\n";
  }
  m_omitParagraph = TRUE;
}

//void RTFGenerator::writeIndexFileItem(const char *,const char *text)
//{
//  t << "\\item\\contentsline{section}{";
//  docify(text);
//  t << "}{\\pageref{" << text << "}}\n";
//}

void RTFGenerator::startHtmlLink(const char *url)
{

  if (Config_getBool(RTF_HYPERLINKS))
  {
    t << "{\\field {\\*\\fldinst { HYPERLINK \"";
    t << url;
    t << "\" }{}";
    t << "}{\\fldrslt {\\cs37\\ul\\cf2 ";
  }
  else
  {
    startTypewriter();
  }
}

void RTFGenerator::endHtmlLink()
{
  if (Config_getBool(RTF_HYPERLINKS))
  {
    t << "}}}\n";
  }
  else
  {
    endTypewriter();
  }
}

//void RTFGenerator::writeMailLink(const char *url)
//{
//  startTypewriter();
//  docify(url);
//  endTypewriter();
//}

void RTFGenerator::writeStartAnnoItem(const char *,const char *f,
    const char *path,const char *name)
{
  DBG_RTF(t << "{\\comment (writeStartAnnoItem)}\n")
  t << "{\\b ";
  if (path) docify(path);
  if (f && Config_getBool(RTF_HYPERLINKS))
  {
    t << "{\\field {\\*\\fldinst { HYPERLINK  \\\\l \"";
    t << rtfFormatBmkStr(f);
    t << "\" }{}";
    t << "}{\\fldrslt {\\cs37\\ul\\cf2 ";

    docify(name);

    t << "}}}\n";
  }
  else
  {
    docify(name);
  }
  t << "} ";
}

void RTFGenerator::writeEndAnnoItem(const char *name)
{
  DBG_RTF(t << "{\\comment (writeEndAnnoItem)}\n")
  if (name)
  {
    t << "\\tab ";
    writeRTFReference(name);
    t << "\n";
  }
  else
  {
    t << "\n";
  }
  newParagraph();
}

void RTFGenerator::startIndexKey()
{
  DBG_RTF(t << "{\\comment (startIndexKey)}\n")
  t << "{\\b ";
}

void RTFGenerator::endIndexKey()
{
  DBG_RTF(t << "{\\comment (endIndexKey)}\n")
}

void RTFGenerator::startIndexValue(bool hasBrief)
{
  DBG_RTF(t << "{\\comment (startIndexValue)}\n")
  t << " ";
  if (hasBrief) t << "(";
}

void RTFGenerator::endIndexValue(const char *name,bool hasBrief)
{
  DBG_RTF(t << "{\\comment (endIndexValue)}\n")
  if (hasBrief) t << ")";
  t << "} ";
  if (name)
  {
    t << "\\tab ";
    writeRTFReference(name);
    t << "\n";
  }
  else
  {
    t << "\n";
  }
  m_omitParagraph=FALSE;
  newParagraph();
}

void RTFGenerator::startSubsection()
{
  //beginRTFSubSection();
  t <<"\n";
  DBG_RTF(t << "{\\comment Begin SubSection}\n")
  t << rtf_Style_Reset;
  t << rtf_Style["Heading3"].reference() << "\n";
}

void RTFGenerator::endSubsection()
{
  newParagraph();
  t << rtf_Style_Reset << "\n";
}

void RTFGenerator::startSubsubsection()
{
  //beginRTFSubSubSection();
  t << "\n";
  DBG_RTF(t << "{\\comment Begin SubSubSection}\n")
  t << "{\n";
  t << rtf_Style_Reset << rtf_Style["Heading4"].reference() << "\n";
}

void RTFGenerator::endSubsubsection()
{
  newParagraph();
  t << "}\n";
}


//void RTFGenerator::writeClassLink(const char *,const char *,
//                                    const char *,const char *name)
//{
//  t << "{\\bf ";
//  docify(name);
//  t << "}";
//}

//void RTFGenerator::startTable(bool,int colNumbers)
//{
//  DBG_RTF(t << "{\\comment startTable}\n";)
//  m_numCols=colNumbers;
//  t << "\\par\n";
//}
//
//void RTFGenerator::endTable(bool hasCaption)
//{
//  DBG_RTF(t << "{\\comment endTable}\n";)
//  if (!hasCaption)
//    t << "\n\\pard \\widctlpar\\intbl\\adjustright\n{\\row }\n";
//  t << "\\pard\n\n";
//}
//
//void  RTFGenerator::startCaption()
//{
//  DBG_RTF(t << "{\\comment startCaption}\n";)
//  endTableRow();
//  t << "\\trowd \\trgaph108\\trleft-108\\trbrdrt\\brdrs\\brdrw10 \\trbrdrl\\brdrs\\brdrw10 \\trbrdrb\\brdrs\\brdrw10 \\trbrdrr\\brdrs\\brdrw10 \\trbrdrh\\brdrs\\brdrw10 \\trbrdrv\\brdrs\\brdrw10\n";
//  t << "\\clvertalt\\clbrdrt\\brdrs\\brdrw10 \\clbrdrl\\brdrs\\brdrw10 \\clbrdrb\\brdrs\\brdrw10 \\clbrdrr \\brdrs\\brdrw10 \\cltxlrtb \\cellx"<<rtf_pageWidth<<"\\pard \\qc\\nowidctlpar\\widctlpar\\intbl\\adjustright \n";
//  nextTableColumn();
//}
//
//void  RTFGenerator::endCaption()
//{
//  DBG_RTF(t << "{\\comment endCaption}\n";)
//  endTableColumn();
//  endTableRow();
//}
//
//void RTFGenerator::nextTableRow()
//{
//  DBG_RTF(t << "{\\comment nextTableRow}\n";)
//  ASSERT(m_numCols>0 && m_numCols<25);
//  uint columnWidth=rtf_pageWidth/m_numCols;
//  t << "\\trowd \\trgaph108\\trleft-108\\trbrdrt\\brdrs\\brdrw10 "
//       "\\trbrdrl\\brdrs\\brdrw10 \\trbrdrb\\brdrs\\brdrw10 "
//       "\\trbrdrr\\brdrs\\brdrw10 \\trbrdrh\\brdrs\\brdrw10 "
//       "\\trbrdrv\\brdrs\\brdrw10 \n";
//  for (int i=0;i<m_numCols;i++)
//  {
//    t << "\\clvertalt\\clbrdrt\\brdrs\\brdrw10 \\clbrdrl\\brdrs\\brdrw10 "
//         "\\clbrdrb\\brdrs\\brdrw10 \\clbrdrr \\brdrs\\brdrw10 \\cltxlrtb "
//         "\\cellx" << (i*columnWidth) << "\n";
//  }
//  t << "\\pard \\widctlpar\\intbl\\adjustright\n{";
//}
//
//void RTFGenerator::endTableRow()
//{
//  DBG_RTF(t << "{\\comment endTableRow}\n";)
//  t << "\n\\pard \\widctlpar\\intbl\\adjustright\n{\\row }\n";
//}
//
//void RTFGenerator::nextTableColumn()
//{
//  DBG_RTF(t << "{\\comment nextTableColumn}\n";)
//  t << "{ ";
//}
//
//void RTFGenerator::endTableColumn()
//{
//  DBG_RTF(t << "{\\comment endTableColumn}\n";)
//  t << " \\cell }";
//}
//
void RTFGenerator::startTextLink(const char *f,const char *anchor)
{
  if (Config_getBool(RTF_HYPERLINKS))
  {
    QCString ref;
    if (f)
    {
      ref+=f;
    }
    if (anchor)
    {
      ref+='_';
      ref+=anchor;
    }

    t << "{\\field {\\*\\fldinst { HYPERLINK  \\\\l \"";
    t << rtfFormatBmkStr(ref);
    t << "\" }{}";
    t << "}{\\fldrslt {\\cs37\\ul\\cf2 ";
  }
}

void RTFGenerator::endTextLink()
{
  if (Config_getBool(RTF_HYPERLINKS))
  {
    t << "}}}\n";
  }
}

void RTFGenerator::writeObjectLink(const char *ref, const char *f,
    const char *anchor, const char *text)
{
  if (!ref && Config_getBool(RTF_HYPERLINKS))
  {
    QCString refName;
    if (f)
    {
      refName+=f;
    }
    if (anchor)
    {
      refName+='_';
      refName+=anchor;
    }

    t << "{\\field {\\*\\fldinst { HYPERLINK  \\\\l \"";
    t << rtfFormatBmkStr(refName);
    t << "\" }{}";
    t << "}{\\fldrslt {\\cs37\\ul\\cf2 ";

    docify(text);

    t << "}}}\n";
  }
  else
  {
    startBold();
    docify(text);
    endBold();
  }
}

void RTFGenerator::startPageRef()
{
  t << " (";
  startEmphasis();
}

void RTFGenerator::endPageRef(const char *clname, const char *anchor)
{
  QCString ref;
  if (clname)
  {
    ref+=clname;
  }
  if (anchor)
  {
    ref+='_';
    ref+=anchor;
  }
  writeRTFReference(ref);
  endEmphasis();
  t << ")";
}

void RTFGenerator::writeCodeLink(const char *ref,const char *f,
                                 const char *anchor,const char *name,
                                 const char *)
{
  if (!ref && Config_getBool(RTF_HYPERLINKS))
  {
    QCString refName;
    if (f)
    {
      refName+=f;
    }
    if (anchor)
    {
      refName+='_';
      refName+=anchor;
    }

    t << "{\\field {\\*\\fldinst { HYPERLINK  \\\\l \"";
    t << rtfFormatBmkStr(refName);
    t << "\" }{}";
    t << "}{\\fldrslt {\\cs37\\ul\\cf2 ";

    codify(name);

    t << "}}}\n";
  }
  else
  {
    codify(name);
  }
}

void RTFGenerator::startTitleHead(const char *)
{
  DBG_RTF(t <<"{\\comment startTitleHead}\n")

  //    beginRTFSection();
  t << rtf_Style_Reset << rtf_Style["Heading2"].reference() << "\n";
}

void RTFGenerator::endTitleHead(const char *fileName,const char *name)
{
  DBG_RTF(t <<"{\\comment endTitleHead}\n")
  t << "\\par " << rtf_Style_Reset << "\n";
  if (name)
  {
    // make table of contents entry
    t << "{\\tc\\tcl2 \\v ";
    docify(name);
    t << "}\n";

    // make an index entry
    addIndexItem(name,0);

    //if (name)
    //{
    //  writeAnchor(0,name);
    //}
    //
    //if (Config_getBool(RTF_HYPERLINKS) && fileName)
    //{
      writeAnchor(fileName,0);
    //}
  }
}

void RTFGenerator::startTitle()
{
  DBG_RTF(t <<"{\\comment startTitle}\n")
  if (Config_getBool(COMPACT_RTF))
    beginRTFSection();
  else
    beginRTFChapter();
}

void RTFGenerator::startGroupHeader(int extraIndent)
{
  DBG_RTF(t <<"{\\comment startGroupHeader}\n")
  //newParagraph();
  t << rtf_Style_Reset;
  if (extraIndent==2)
  {
    t << rtf_Style["Heading5"].reference();
  }
  else if (extraIndent==1)
  {
    t << rtf_Style["Heading4"].reference();
  }
  else // extraIndent==0
  {
    t << rtf_Style["Heading3"].reference();
  }
  t << "\n";
}

void RTFGenerator::endGroupHeader(int)
{
  DBG_RTF(t <<"{\\comment endGroupHeader}\n")
  t << "\\par\n";
  t << rtf_Style_Reset << "\n";
}

void RTFGenerator::startMemberDoc(const char *clname,
    const char *memname,
    const char *,
    const char *,
    int,
    int,
    bool showInline)
{
  DBG_RTF(t << "{\\comment startMemberDoc}\n")
  if (memname && memname[0]!='@')
  {
    addIndexItem(memname,clname);
    addIndexItem(clname,memname);
  }
  t << rtf_Style_Reset << rtf_Style[showInline ? "Heading5" : "Heading4"].reference();
  //styleStack.push(rtf_Style_Heading4);
  t << "{\n";
  //printf("RTFGenerator::startMemberDoc() '%s'\n",rtf_Style["Heading4"].reference());
  startBold();
  t << "\n";
}

void RTFGenerator::endMemberDoc(bool)
{
  DBG_RTF(t << "{\\comment endMemberDoc}\n")
  //const char *style = styleStack.pop();
  //printf("RTFGenerator::endMemberDoc() '%s'\n",style);
  //ASSERT(style==rtf_Style["Heading4"].reference());
  endBold();
  t << "}\n";
  newParagraph();
}

void RTFGenerator::startDoxyAnchor(const char *,const char *,
                                   const char *,const char *,
                                   const char *
                                  )
{
  DBG_RTF(t << "{\\comment startDoxyAnchor}\n")
}

void RTFGenerator::endDoxyAnchor(const char *fName,const char *anchor)
{
  QCString ref;
  if (fName)
  {
    ref+=fName;
  }
  if (anchor)
  {
    ref+='_';
    ref+=anchor;
  }

  DBG_RTF(t << "{\\comment endDoxyAnchor}\n")
  t << "{\\bkmkstart ";
  t << rtfFormatBmkStr(ref);
  t << "}\n";
  t << "{\\bkmkend ";
  t << rtfFormatBmkStr(ref);
  t << "}\n";
}


//void RTFGenerator::writeLatexLabel(const char *clName,const char *anchor)
//{
//  writeDoxyAnchor(0,clName,anchor,0);
//}

void RTFGenerator::addIndexItem(const char *s1,const char *s2)
{
  if (s1)
  {
    t << "{\\xe \\v ";
    docify(s1);
    if (s2)
    {
      t << "\\:";
      docify(s2);
    }
    t << "}\n";
  }
}

void RTFGenerator::startIndent()
{
  incrementIndentLevel();
  DBG_RTF(t << "{\\comment (startIndent) }\n")
  t << "{\n";
  t << rtf_Style_Reset << rtf_CList_DepthStyle() << "\n";
}

void RTFGenerator::endIndent()
{
  t << "}\n";
  decrementIndentLevel();
}


void RTFGenerator::startDescription()
{
  DBG_RTF(t << "{\\comment (startDescription)}"    << "\n")
  t << "{\n";
  t << rtf_Style_Reset << rtf_DList_DepthStyle();
}

void RTFGenerator::endDescription()
{
  DBG_RTF(t << "{\\comment (endDescription)}"    << "\n")
  newParagraph();
  t << "}";
}

void RTFGenerator::startDescItem()
{
  newParagraph();
  DBG_RTF(t << "{\\comment (startDescItem)}\n")
  t << "{\\b ";
}

void RTFGenerator::endDescItem()
{
  DBG_RTF(t << "{\\comment (endDescItem)}\n")
  t << "}\n";
  newParagraph();
}

void RTFGenerator::startMemberDescription(const char *,const char *,bool)
{
  DBG_RTF(t << "{\\comment (startMemberDescription)}\n")
  t << "{\n";
  incrementIndentLevel();
  t << rtf_Style_Reset << rtf_CList_DepthStyle();
  startEmphasis();
}

void RTFGenerator::endMemberDescription()
{
  DBG_RTF(t << "{\\comment (endMemberDescription)}\n")
  endEmphasis();
  //newParagraph();
  decrementIndentLevel();
  t << "\\par";
  t << "}\n";
  m_omitParagraph = TRUE;
}

void RTFGenerator::startDescList(SectionTypes)
{
  DBG_RTF(t << "{\\comment (startDescList)}\n")
  t << "{"; // ends at endDescList
  t << "{"; // ends at endDescTitle
  startBold();
  newParagraph();
}

//void RTFGenerator::endDescTitle()
//{
//  DBG_RTF(t << "{\\comment (endDescTitle) }\n")
//  endBold();
//  t << "}";
//  newParagraph();
//  incrementIndentLevel();
//  t << rtf_Style_Reset << rtf_DList_DepthStyle();
//}

void RTFGenerator::startDescForItem()
{
  DBG_RTF(t << "{\\comment (startDescForItem) }\n")
}

void RTFGenerator::endDescForItem()
{
  DBG_RTF(t << "{\\comment (endDescForItem) }\n")
}

//void RTFGenerator::endDescList()
//{
//  DBG_RTF(t << "{\\comment (endDescList)}\n")
//  newParagraph();
//  decrementIndentLevel();
//  m_omitParagraph = TRUE;
//  t << "}";
//}


void RTFGenerator::startSection(const char *,const char *title,SectionType type)
{
  DBG_RTF(t << "{\\comment (startSection)}\n")
  t << "{";
  t<< rtf_Style_Reset;
  int num=4;
  switch(type)
  {
    case SectionType::Page:          num=2; break;
    case SectionType::Section:       num=3; break;
    case SectionType::Subsection:    num=4; break;
    case SectionType::Subsubsection: num=4; break;
    case SectionType::Paragraph:     num=4; break;
    default: ASSERT(0); break;
  }
  QCString heading;
  heading.sprintf("Heading%d",num);
  // set style
  t << rtf_Style[heading.str()].reference();
  // make table of contents entry
  t << "{\\tc\\tcl" << num << " \\v ";
  docify(title);
  t << "}\n";
}

void RTFGenerator::endSection(const char *lab,SectionType)
{
  DBG_RTF(t << "{\\comment (endSection)}\n")
  // make bookmark
  m_omitParagraph=FALSE;
  newParagraph();
  writeAnchor(0,lab);
  t << "}";
}

//void RTFGenerator::writeSectionRef(const char *ref,const char *,
//                                   const char *lab,const char *title)
//{
//  if (ref)
//  {
//    docify(title);
//  }
//  else
//  {
//    startBold();
//    docify(title);
//    endBold();
//    t << " (";
//    docify(theTranslator->trPageAbbreviation());
//    writeRTFReference(lab);
//    t << ")\n";
//  }
//}
//
//void RTFGenerator::writeSectionRefItem(const char *,const char *lab,
//    const char *title)
//{
//  docify(title);
//  t << "\\tab";
//  writeRTFReference(lab);
//  t << "\n";
//}
//
//void RTFGenerator::writeSectionRefAnchor(const char *name,const char *lab,
//    const char *title)
//{
//  writeSectionRef(name,lab,title);
//}

//char* RTFGenerator::getMultiByte(int c)
//{
//    static char s[10];
//
//    sprintf(s,"\\'%X",c);
//    return s;
//}

void RTFGenerator::docify(const char *str)
{
  if (str)
  {
    const unsigned char *p=(const unsigned char *)str;
    unsigned char c;
    //unsigned char pc='\0';
    while (*p)
    {
      //static bool MultiByte = FALSE;
      c=*p++;

#if 0
      if ( MultiByte )
      {
        t << getMultiByte( c );
        MultiByte = FALSE;
        continue;
      }
      if ( c >= 0x80 )
      {
        MultiByte = TRUE;
        t << getMultiByte( c );
        continue;
      }
#endif

      switch (c)
      {
        case '{':  t << "\\{";            break;
        case '}':  t << "\\}";            break;
        case '\\': t << "\\\\";           break;
        default:
          {
            // see if we can insert an hyphenation hint
            //if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-";
            t << (char)c;
          }
      }
      //pc = c;
      m_omitParagraph = FALSE;
    }
  }
}

void RTFGenerator::codify(const char *str)
{
  // note that RTF does not have a "verbatim", so "\n" means
  // nothing... add a "newParagraph()";
  //static char spaces[]="        ";
  if (str)
  {
    const unsigned char *p=(const unsigned char *)str;
    unsigned char c;
    int spacesToNextTabStop;

    while (*p)
    {
      //static bool MultiByte = FALSE;

      c=*p++;

      switch(c)
      {
        case '\t':  spacesToNextTabStop = Config_getInt(TAB_SIZE) - (m_col%Config_getInt(TAB_SIZE));
                    t << Doxygen::spaces.left(spacesToNextTabStop);
                    m_col+=spacesToNextTabStop;
                    break;
        case '\n':  newParagraph();
                    t << '\n'; m_col=0;
                    break;
        case '{':   t << "\\{"; m_col++;          break;
        case '}':   t << "\\}"; m_col++;          break;
        case '\\':  t << "\\\\"; m_col++;         break;
        default:    p=(const unsigned char *)writeUtf8Char(t,(const char *)p-1); m_col++; break;
      }
    }
  }
}

void RTFGenerator::writeChar(char c)
{
  char cs[2];
  cs[0]=c;
  cs[1]=0;
  docify(cs);
}

void RTFGenerator::startClassDiagram()
{
  DBG_RTF(t <<"{\\comment startClassDiagram }\n")
}

void RTFGenerator::endClassDiagram(const ClassDiagram &d,
    const char *fileName,const char *)
{
  newParagraph();

  // create a png file
  d.writeImage(t,dir(),m_relPath,fileName,FALSE);

  // display the file
  t << "{\n";
  t << rtf_Style_Reset << "\n";
  t << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \"";
  t << fileName << ".png\"";
  t << " \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par\n";
  t << "}\n";
}

//void RTFGenerator::writeFormula(const char *,const char *text)
//{
//  t << text;
//}

void RTFGenerator::startMemberItem(const char *,int,const char *)
{
  DBG_RTF(t <<"{\\comment startMemberItem }\n")
  t << rtf_Style_Reset << rtf_BList_DepthStyle() << "\n"; // set style to appropriate depth
}

void RTFGenerator::endMemberItem()
{
  DBG_RTF(t <<"{\\comment endMemberItem }\n")
  newParagraph();
}

void RTFGenerator::writeAnchor(const char *fileName,const char *name)
{
  QCString anchor;
  if (fileName)
  {
    anchor+=fileName;
  }
  if (fileName && name)
  {
    anchor+='_';
  }
  if (name)
  {
    anchor+=name;
  }

  DBG_RTF(t <<"{\\comment writeAnchor (" << anchor << ")}\n")
  t << "{\\bkmkstart " << rtfFormatBmkStr(anchor) << "}\n";
  t << "{\\bkmkend " << rtfFormatBmkStr(anchor) << "}\n";
}

void RTFGenerator::writeRTFReference(const char *label)
{
  t << "{\\field\\fldedit {\\*\\fldinst PAGEREF ";
  t << rtfFormatBmkStr(label);
  t << " \\\\*MERGEFORMAT}{\\fldrslt pagenum}}";
}

void RTFGenerator::startCodeFragment(const char *)
{
  DBG_RTF(t << "{\\comment (startCodeFragment) }\n")
  t << "{\n";
  t << rtf_Style_Reset << rtf_Code_DepthStyle();
}

void RTFGenerator::endCodeFragment(const char *)
{
  endCodeLine();

  DBG_RTF(t << "{\\comment (endCodeFragment) }\n")
  t << "}\n";
  m_omitParagraph = TRUE;
}

void RTFGenerator::writeNonBreakableSpace(int)
{
  t << "\\~ ";
}


void RTFGenerator::startMemberList()
{
  t << "\n";
  DBG_RTF(t << "{\\comment (startMemberList) }\n")
  t << "{\n";
#ifdef DELETEDCODE
  if (!insideTabbing)
    t << "\\begin{CompactItemize}\n";
#endif
}

void RTFGenerator::endMemberList()
{
  DBG_RTF(t << "{\\comment (endMemberList) }\n")
  t << "}\n";
#ifdef DELETEDCODE
  if (!insideTabbing)
    t << "\\end{CompactItemize}\n";
#endif
}

//void RTFGenerator::startImage(const char *name,const char *,bool)
//{
//  newParagraph();
//  t << "{\n";
//  t << rtf_Style_Reset << "\n";
//  t << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE ";
//  t << name;
//  t << " \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par\n";
//  t << "}\n";
//}
//
//void RTFGenerator::endImage(bool)
//{
//  // not yet implemented
//}
//
//void RTFGenerator::startDotFile(const char *name,bool)
//{
//  QCString baseName=name;
//  int i;
//  if ((i=baseName.findRev('/'))!=-1 || (i=baseName.findRev('\\'))!=-1)
//  {
//    baseName=baseName.right(baseName.length()-i-1);
//  }
//  QCString outDir = Config_getString(RTF_OUTPUT);
//  writeDotGraphFromFile(name,outDir,baseName,BITMAP);
//  newParagraph();
//  t << "{\n";
//  t << rtf_Style_Reset << "\n";
//  t << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE ";
//  t << outDir << "\\" << baseName;
//  t << " \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par\n";
//  t << "}\n";
//}
//
//void RTFGenerator::endDotFile(bool)
//{
//  // not yet implemented
//}
//
void RTFGenerator::startDescTable(const char *title)
{
  DBG_RTF(t << "{\\comment (startDescTable) }\n")
  t << "{\\par\n";
  t << "{" << rtf_Style["Heading5"].reference() << "\n";
  docify(title);
  t << ":\\par}\n";
  t << rtf_Style_Reset << rtf_DList_DepthStyle();
  t << "\\trowd \\trgaph108\\trleft426\\tblind426"
       "\\trbrdrt\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrl\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrb\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrr\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrh\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrv\\brdrs\\brdrw10\\brdrcf15 \n";
  int i,columnPos[2] = { 25, 100 };
  for (i=0;i<2;i++)
  {
    t << "\\clvertalt\\clbrdrt\\brdrs\\brdrw10\\brdrcf15 "
         "\\clbrdrl\\brdrs\\brdrw10\\brdrcf15 "
         "\\clbrdrb\\brdrs\\brdrw10\\brdrcf15 "
         "\\clbrdrr \\brdrs\\brdrw10\\brdrcf15 "
         "\\cltxlrtb "
         "\\cellx" << (rtf_pageWidth*columnPos[i]/100) << "\n";
  }
  t << "\\pard \\widctlpar\\intbl\\adjustright\n";
}

void RTFGenerator::endDescTable()
{
  DBG_RTF(t << "{\\comment (endDescTable)}\n")
  t << "}\n";
}

void RTFGenerator::startDescTableRow()
{
}

void RTFGenerator::endDescTableRow()
{
}

void RTFGenerator::startDescTableTitle()
{
  DBG_RTF(t << "{\\comment (startDescTableTitle) }\n")
  t << "{\\qr ";
}

void RTFGenerator::endDescTableTitle()
{
  DBG_RTF(t << "{\\comment (endDescTableTitle) }\n")
  t << "\\cell }";
}

void RTFGenerator::startDescTableData()
{
  DBG_RTF(t << "{\\comment (startDescTableData) }\n")
  t << "{";
}

void RTFGenerator::endDescTableData()
{
  DBG_RTF(t << "{\\comment (endDescTableData) }\n")
  t << "\\cell }{\\row }\n";
}

// a style for list formatted as a "bulleted list"

void RTFGenerator::incrementIndentLevel()
{
  m_listLevel++;
  if (m_listLevel>rtf_maxIndentLevels-1)
  {
    err("Maximum indent level (%d) exceeded while generating RTF output!\n",rtf_maxIndentLevels);
    m_listLevel=rtf_maxIndentLevels-1;
  }
}

void RTFGenerator::decrementIndentLevel()
{
  m_listLevel--;
  if (m_listLevel<0)
  {
    err("Negative indent level while generating RTF output!\n");
    m_listLevel=0;
  }
}

// a style for list formatted with "list continue" style
const char * RTFGenerator::rtf_CList_DepthStyle()
{
  QCString n=makeIndexName("ListContinue",m_listLevel);
  return rtf_Style[n.str()].reference();
}

// a style for list formatted as a "latext style" table of contents
const char * RTFGenerator::rtf_LCList_DepthStyle()
{
  QCString n=makeIndexName("LatexTOC",m_listLevel);
  return rtf_Style[n.str()].reference();
}

// a style for list formatted as a "bullet" style
const char * RTFGenerator::rtf_BList_DepthStyle()
{
  QCString n=makeIndexName("ListBullet",m_listLevel);
  return rtf_Style[n.str()].reference();
}

// a style for list formatted as a "enumeration" style
const char * RTFGenerator::rtf_EList_DepthStyle()
{
  QCString n=makeIndexName("ListEnum",m_listLevel);
  return rtf_Style[n.str()].reference();
}

const char * RTFGenerator::rtf_DList_DepthStyle()
{
  QCString n=makeIndexName("DescContinue",m_listLevel);
  return rtf_Style[n.str()].reference();
}

const char * RTFGenerator::rtf_Code_DepthStyle()
{
  QCString n=makeIndexName("CodeExample",m_listLevel);
  return rtf_Style[n.str()].reference();
}

void RTFGenerator::startTextBlock(bool dense)
{
  DBG_RTF(t << "{\\comment startTextBlock}\n")
  t << "{\n";
  t << rtf_Style_Reset;
  if (dense) // no spacing between "paragraphs"
  {
    t << rtf_Style["DenseText"].reference();
  }
  else // some spacing
  {
    t << rtf_Style["BodyText"].reference();
  }
}

void RTFGenerator::endTextBlock(bool /*paraBreak*/)
{
  newParagraph();
  DBG_RTF(t << "{\\comment endTextBlock}\n")
  t << "}\n";
  //m_omitParagraph = TRUE;
}

void RTFGenerator::newParagraph()
{
  if (!m_omitParagraph)
  {
    DBG_RTF(t << "{\\comment (newParagraph)}\n")
    t << "\\par\n";
  }
  m_omitParagraph = FALSE;
}

void RTFGenerator::startParagraph(const char *txt)
{
  DBG_RTF(t << "{\\comment startParagraph}\n")
  newParagraph();
  t << "{\n";
  if (QCString(txt) == "reference") t << "\\ql\n";
}

void RTFGenerator::endParagraph()
{
  DBG_RTF(t << "{\\comment endParagraph}\n")
  t << "}\\par\n";
  m_omitParagraph = TRUE;
}

void RTFGenerator::startMemberSubtitle()
{
  DBG_RTF(t << "{\\comment startMemberSubtitle}\n")
  t << "{\n";
  t << rtf_Style_Reset << rtf_CList_DepthStyle() << "\n";
}

void RTFGenerator::endMemberSubtitle()
{
  DBG_RTF(t << "{\\comment endMemberSubtitle}\n")
  newParagraph();
  t << "}\n";
}

//void RTFGenerator::writeUmlaut(char c)
//{
//  switch(c)
//  {
//    case 'A' : t << '\304'; break;
//    case 'E' : t << '\313'; break;
//    case 'I' : t << '\317'; break;
//    case 'O' : t << '\326'; break;
//    case 'U' : t << '\334'; break;
//    case 'Y' : t << 'Y';    break;
//    case 'a' : t << '\344'; break;
//    case 'e' : t << '\353'; break;
//    case 'i' : t << '\357'; break;
//    case 'o' : t << '\366'; break;
//    case 'u' : t << '\374'; break;
//    case 'y' : t << '\377'; break;
//    default: t << '?'; break;
//  }
//}
//
//void RTFGenerator::writeAcute(char c)
//{
//  switch(c)
//  {
//    case 'A' : t << '\301'; break;
//    case 'E' : t << '\311'; break;
//    case 'I' : t << '\315'; break;
//    case 'O' : t << '\323'; break;
//    case 'U' : t << '\332'; break;
//    case 'Y' : t << '\335'; break;
//    case 'a' : t << '\341'; break;
//    case 'e' : t << '\351'; break;
//    case 'i' : t << '\355'; break;
//    case 'o' : t << '\363'; break;
//    case 'u' : t << '\372'; break;
//    case 'y' : t << '\375'; break;
//    default: t << '?'; break;
//  }
//}
//
//void RTFGenerator::writeGrave(char c)
//{
//  switch(c)
//  {
//    case 'A' : t << '\300'; break;
//    case 'E' : t << '\310'; break;
//    case 'I' : t << '\314'; break;
//    case 'O' : t << '\322'; break;
//    case 'U' : t << '\331'; break;
//    case 'a' : t << '\340'; break;
//    case 'e' : t << '\350'; break;
//    case 'i' : t << '\354'; break;
//    case 'o' : t << '\362'; break;
//    case 'u' : t << '\371'; break;
//    default: t << '?'; break;
//  }
//}
//
//void RTFGenerator::writeCirc(char c)
//{
//  switch(c)
//  {
//    case 'A' : t << '\302'; break;
//    case 'E' : t << '\312'; break;
//    case 'I' : t << '\316'; break;
//    case 'O' : t << '\324'; break;
//    case 'U' : t << '\333'; break;
//    case 'a' : t << '\342'; break;
//    case 'e' : t << '\352'; break;
//    case 'i' : t << '\356'; break;
//    case 'o' : t << '\364'; break;
//    case 'u' : t << '\373'; break;
//    default: t << '?'; break;
//  }
//}
//
//void RTFGenerator::writeTilde(char c)
//{
//  switch(c)
//  {
//    case 'A' : t << '\303'; break;
//    case 'N' : t << '\321'; break;
//    case 'O' : t << '\325'; break;
//    case 'a' : t << '\343'; break;
//    case 'n' : t << '\361'; break;
//    case 'o' : t << '\365'; break;
//    default: t << '?'; break;
//  }
//}
//
//void RTFGenerator::writeRing(char c)
//{
//  switch(c)
//  {
//    case 'A' : t << '\305'; break;
//    case 'a' : t << '\345'; break;
//    default: t << '?'; break;
//  }
//}
//
//void RTFGenerator::writeCCedil(char c)
//{
//  switch(c)
//  {
//    case 'C' : t << '\307'; break;
//    case 'c' : t << '\347'; break;
//    default: t << '?'; break;
//  }
//}
//

bool isLeadBytes(int c)
{
  bool result;

  QCString codePage = theTranslator->trRTFansicp();

  if (codePage == "932")       // cp932 (Japanese Shift-JIS)
  {
    result = (0x81<=c && c<=0x9f) || (0xe0<=c && c<=0xfc);
  }
  else if (codePage == "936")  // cp936 (Simplified Chinese GBK)
  {
    result = 0x81<=c && c<=0xFE;
  }
  else if (codePage == "949")  // cp949 (Korean)
  {
    result = 0x81<=c && c<=0xFE;
  }
  else if (codePage == "950")  // cp950 (Traditional Chinese Big5)
  {
    result = 0x81<=c && c<=0xFE;
  }
  else                         // for SBCS Codepages (cp1252,1251 etc...)
  {
    result = false;
  }

  return result;
}


// note: function is not reentrant!
static void encodeForOutput(std::ostream &t,const char *s)
{
  if (s==0) return;
  QCString encoding;
  bool converted=FALSE;
  int l = qstrlen(s);
  static QByteArray enc;
  if (l*4>(int)enc.size()) enc.resize(l*4); // worst case
  encoding.sprintf("CP%s",theTranslator->trRTFansicp().data());
  if (!encoding.isEmpty())
  {
    // convert from UTF-8 back to the output encoding
    void *cd = portable_iconv_open(encoding,"UTF-8");
    if (cd!=(void *)(-1))
    {
      size_t iLeft=l;
      size_t oLeft=enc.size();
      char *inputPtr = (char*)s;
      char *outputPtr = enc.data();
      if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
      {
        enc.resize(enc.size()-(unsigned int)oLeft);
        converted=TRUE;
      }
      portable_iconv_close(cd);
    }
  }
  if (!converted) // if we did not convert anything, copy as is.
  {
    memcpy(enc.data(),s,l);
    enc.resize(l);
  }
  uint i;
  bool multiByte = FALSE;

  for (i=0;i<enc.size();i++)
  {
    uchar c = (uchar)enc.at(i);

    if (c>=0x80 || multiByte)
    {
      char esc[10];
      sprintf(esc,"\\'%X",c);        // escape sequence for SBCS and DBCS(1st&2nd bytes).
      t << esc;

      if (!multiByte)
      {
        multiByte = isLeadBytes(c);  // It may be DBCS Codepages.
      }
      else
      {
        multiByte = FALSE;           // end of Double Bytes Character.
      }
    }
    else
    {
      t << (char)c;
    }
  }
}

/**
 * VERY brittle routine inline RTF's included by other RTF's.
 * it is recursive and ugly.
 */
static bool preProcessFile(Dir &d,const QCString &infName, std::ostream &t, bool bIncludeHeader=TRUE)
{
  std::ifstream f(infName.str(),std::ifstream::in);
  if (!f.is_open())
  {
    err("problems opening rtf file %s for reading\n",infName.data());
    return false;
  }

  const int maxLineLength = 10240;
  static QCString lineBuf(maxLineLength);

  // scan until find end of header
  // this is EXTREEEEEEEMLY brittle.  It works on OUR rtf
  // files because the first line before the body
  // ALWAYS contains "{\comment begin body}"
  std::string line;
  while (getline(f,line))
  {
    line+='\n';
    if (line.find("\\comment begin body")!=std::string::npos) break;
    if (bIncludeHeader) encodeForOutput(t,line.c_str());
  }

  std::string prevLine;
  bool first=true;
  while (getline(f,line))
  {
    line+='\n';
    size_t pos;
    if ((pos=prevLine.find("INCLUDETEXT"))!=std::string::npos)
    {
      size_t startNamePos  = prevLine.find('"',pos)+1;
      size_t endNamePos    = prevLine.find('"',startNamePos);
      std::string fileName = prevLine.substr(startNamePos,endNamePos-startNamePos);
      DBG_RTF(t << "{\\comment begin include " << fileName << "}\n")
      if (!preProcessFile(d,fileName.c_str(),t,FALSE)) return FALSE;
      DBG_RTF(t << "{\\comment end include " << fileName << "}\n")
    }
    else if (!first) // no INCLUDETEXT on this line
    {
      encodeForOutput(t,prevLine.c_str());
    }
    prevLine = line;
    first=false;
  }
  if (!bIncludeHeader) // skip final '}' in case we don't include headers
  {
    size_t pos = line.rfind('}');
    if (pos==std::string::npos)
    {
      err("Strange, the last char was not a '}'\n");
      pos = line.length();
    }
    encodeForOutput(t,line.substr(0,pos).c_str());
  }
  else
  {
    encodeForOutput(t,line.c_str());
  }
  f.close();
  // remove temporary file
  d.remove(infName.str());
  return TRUE;
}

void RTFGenerator::startDotGraph()
{
  DBG_RTF(t << "{\\comment (startDotGraph)}\n")
}

void RTFGenerator::endDotGraph(DotClassGraph &g)
{
  newParagraph();

  QCString fn =
    g.writeGraph(t,GOF_BITMAP,EOF_Rtf,dir(),fileName(),m_relPath,TRUE,FALSE);

  // display the file
  t << "{\n";
  t << rtf_Style_Reset << "\n";
  t << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \"";
  QCString imgExt = getDotImageExtension();
  t << fn << "." << imgExt;
  t << "\" \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par\n";
  t << "}\n";
  newParagraph();
  DBG_RTF(t << "{\\comment (endDotGraph)}\n")
}

void RTFGenerator::startInclDepGraph()
{
  DBG_RTF(t << "{\\comment (startInclDepGraph)}\n")
}

void RTFGenerator::endInclDepGraph(DotInclDepGraph &g)
{
  newParagraph();

  QCString fn = g.writeGraph(t,GOF_BITMAP,EOF_Rtf,dir(),fileName(),m_relPath,FALSE);

  // display the file
  t << "{\n";
  t << rtf_Style_Reset << "\n";
  t << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \"";
  QCString imgExt = getDotImageExtension();
  t << fn << "." << imgExt;
  t << "\" \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par\n";
  t << "}\n";
  DBG_RTF(t << "{\\comment (endInclDepGraph)}\n")
}

void RTFGenerator::startGroupCollaboration()
{
}

void RTFGenerator::endGroupCollaboration(DotGroupCollaboration &)
{
}

void RTFGenerator::startCallGraph()
{
  DBG_RTF(t << "{\\comment (startCallGraph)}\n")
}

void RTFGenerator::endCallGraph(DotCallGraph &g)
{
  newParagraph();

  QCString fn = g.writeGraph(t,GOF_BITMAP,EOF_Rtf,dir(),fileName(),m_relPath,FALSE);

  // display the file
  t << "{\n";
  t << rtf_Style_Reset << "\n";
  t << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \"";
  QCString imgExt = getDotImageExtension();
  t << fn << "." << imgExt;
  t << "\" \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par\n";
  t << "}\n";
  DBG_RTF(t << "{\\comment (endCallGraph)}\n")
}

void RTFGenerator::startDirDepGraph()
{
  DBG_RTF(t << "{\\comment (startDirDepGraph)}\n")
}

void RTFGenerator::endDirDepGraph(DotDirDeps &g)
{
  newParagraph();

  QCString fn = g.writeGraph(t,GOF_BITMAP,EOF_Rtf,dir(),fileName(),m_relPath,FALSE);

  // display the file
  t << "{\n";
  t << rtf_Style_Reset << "\n";
  t << "\\par\\pard \\qc {\\field\\flddirty {\\*\\fldinst INCLUDEPICTURE \"";
  QCString imgExt = getDotImageExtension();
  t << fn << "." << imgExt;
  t << "\" \\\\d \\\\*MERGEFORMAT}{\\fldrslt IMAGE}}\\par\n";
  t << "}\n";
  DBG_RTF(t << "{\\comment (endDirDepGraph)}\n")
}

/** Tests the integrity of the result by counting brackets.
 *
 */
void testRTFOutput(const char *name)
{
  int bcount=0;
  int line=1;
  int c;
  std::ifstream f(name,std::ifstream::in);
  if (f.is_open())
  {
    while ((c=f.get())!=-1)
    {
      if (c=='\\') // escape char
      {
        c=f.get();
        if (c==-1) break;
      }
      else if (c=='{') // open bracket
      {
        bcount++;
      }
      else if (c=='}') // close bracket
      {
        bcount--;
        if (bcount<0)
        {
          goto err;
          break;
        }
      }
      else if (c=='\n') // newline
      {
        line++;
      }
    }
  }
  if (bcount==0) return; // file is OK.
err:
  err("RTF integrity test failed at line %d of %s due to a bracket mismatch.\n"
      "       Please try to create a small code example that produces this error \n"
      "       and send that to doxygen@gmail.com.\n",line,name);
}

/**
 * This is an API to a VERY brittle RTF preprocessor that combines nested
 * RTF files.  This version replaces the infile with the new file
 */
bool RTFGenerator::preProcessFileInplace(const char *path,const char *name)
{
  Dir d(path);
  // store the original directory
  if (!d.exists())
  {
    err("Output dir %s does not exist!\n",path);
    return FALSE;
  }
  std::string oldDir = Dir::currentDirPath();

  // go to the html output directory (i.e. path)
  Dir::setCurrent(d.absPath());
  Dir thisDir;

  QCString combinedName = (QCString)path+"/combined.rtf";
  QCString mainRTFName  = (QCString)path+"/"+name;

  std::ofstream outt(combinedName.str(),std::ofstream::out);
  if (!outt.is_open())
  {
    err("Failed to open %s for writing!\n",combinedName.data());
    Dir::setCurrent(oldDir);
    return FALSE;
  }

  if (!preProcessFile(thisDir,mainRTFName,outt))
  {
    // it failed, remove the temp file
    outt.close();
    thisDir.remove(combinedName.str());
    Dir::setCurrent(oldDir);
    return FALSE;
  }

  // everything worked, move the files
  outt.close();
  thisDir.remove(mainRTFName.str());
  thisDir.rename(combinedName.str(),mainRTFName.str());

  testRTFOutput(mainRTFName);

  Dir::setCurrent(oldDir);
  return TRUE;
}

void RTFGenerator::startMemberGroupHeader(bool hasHeader)
{
  DBG_RTF(t << "{\\comment startMemberGroupHeader}\n")
  t << "{\n";
  if (hasHeader) incrementIndentLevel();
  t << rtf_Style_Reset << rtf_Style["GroupHeader"].reference();
}

void RTFGenerator::endMemberGroupHeader()
{
  DBG_RTF(t << "{\\comment endMemberGroupHeader}\n")
  newParagraph();
  t << rtf_Style_Reset << rtf_CList_DepthStyle();
}

void RTFGenerator::startMemberGroupDocs()
{
  DBG_RTF(t << "{\\comment startMemberGroupDocs}\n")
  startEmphasis();
}

void RTFGenerator::endMemberGroupDocs()
{
  DBG_RTF(t << "{\\comment endMemberGroupDocs}\n")
  endEmphasis();
  newParagraph();
}

void RTFGenerator::startMemberGroup()
{
  DBG_RTF(t << "{\\comment startMemberGroup}\n")
  t << rtf_Style_Reset << rtf_BList_DepthStyle() << "\n";
}

void RTFGenerator::endMemberGroup(bool hasHeader)
{
  DBG_RTF(t << "{\\comment endMemberGroup}\n")
  if (hasHeader) decrementIndentLevel();
  t << "}";
}

void RTFGenerator::startExamples()
{
  DBG_RTF(t << "{\\comment (startExamples)}\n")
  t << "{"; // ends at endDescList
  t << "{"; // ends at endDescTitle
  startBold();
  newParagraph();
  docify(theTranslator->trExamples());
  endBold();
  t << "}";
  newParagraph();
  incrementIndentLevel();
  t << rtf_Style_Reset << rtf_DList_DepthStyle();
}

void RTFGenerator::endExamples()
{
  DBG_RTF(t << "{\\comment (endExamples)}\n")
  m_omitParagraph = FALSE;
  newParagraph();
  decrementIndentLevel();
  m_omitParagraph = TRUE;
  t << "}";
}

void RTFGenerator::startParamList(ParamListTypes,const char *title)
{
  DBG_RTF(t << "{\\comment (startParamList)}\n")
  t << "{"; // ends at endParamList
  t << "{"; // ends at endDescTitle
  startBold();
  newParagraph();
  docify(title);
  endBold();
  t << "}";
  newParagraph();
  incrementIndentLevel();
  t << rtf_Style_Reset << rtf_DList_DepthStyle();
}

void RTFGenerator::endParamList()
{
  DBG_RTF(t << "{\\comment (endParamList)}\n")
  newParagraph();
  decrementIndentLevel();
  m_omitParagraph = TRUE;
  t << "}";
}

void RTFGenerator::startParameterType(bool first,const char *key)
{
  DBG_RTF(t << "{\\comment (startParameterType)}\n")
  if (!first && key)
  {
    t << " " << key << " ";
  }
}

void RTFGenerator::endParameterType()
{
  DBG_RTF(t << "{\\comment (endParameterType)}\n")
  t << " ";
}

void RTFGenerator::exceptionEntry(const char* prefix,bool closeBracket)
{
  DBG_RTF(t << "{\\comment (exceptionEntry)}\n")
  if (prefix)
      t << " " << prefix << "(";
  else if (closeBracket)
      t << ")";
  t << " ";
}

void RTFGenerator::writeDoc(DocNode *n,const Definition *ctx,const MemberDef *,int)
{
  RTFDocVisitor *visitor = new RTFDocVisitor(t,*this,ctx?ctx->getDefFileExtension():QCString(""));
  n->accept(visitor);
  delete visitor;
  m_omitParagraph = TRUE;
}

void RTFGenerator::rtfwriteRuler_doubleline()
{
  DBG_RTF(t << "{\\comment (rtfwriteRuler_doubleline)}\n")
  t << "{\\pard\\widctlpar\\brdrb\\brdrdb\\brdrw15\\brsp20 \\adjustright \\par}\n";
}

void RTFGenerator::rtfwriteRuler_emboss()
{
  DBG_RTF(t << "{\\comment (rtfwriteRuler_emboss)}\n")
  t << "{\\pard\\widctlpar\\brdrb\\brdremboss\\brdrw15\\brsp20 \\adjustright \\par}\n";
}

void RTFGenerator::rtfwriteRuler_thick()
{
  DBG_RTF(t << "{\\comment (rtfwriteRuler_thick)}\n")
  t << "{\\pard\\widctlpar\\brdrb\\brdrs\\brdrw75\\brsp20 \\adjustright \\par}\n";
}

void RTFGenerator::rtfwriteRuler_thin()
{
  DBG_RTF(t << "{\\comment (rtfwriteRuler_thin)}\n")
  t << "{\\pard\\widctlpar\\brdrb\\brdrs\\brdrw5\\brsp20 \\adjustright \\par}\n";
}

#if 0
void RTFGenerator::postProcess(QByteArray &a)
{
  QByteArray enc(a.size()*4); // worst case
  int off=0;
  uint i;
  bool mbFlag=FALSE;
  for (i=0;i<a.size();i++)
  {
    unsigned char c = (unsigned char)a.at(i);

    // treat characters > 0x80 as multibyte characters, except when they
    // are control characters
    if (c>0x80 || (mbFlag && c!='\\' && c!='{' && c!='}'))
    {
      char s[10];
      sprintf(s,"\\'%X",c);
      qstrcpy(enc.data()+off,s);
      off+=qstrlen(s);
      mbFlag=c>0x80;
    }
    else
    {
      enc.at(off++)=c;
    }
  }
  enc.resize(off);
  a = enc;
}
#endif

void RTFGenerator::startConstraintList(const char *header)
{
  DBG_RTF(t << "{\\comment (startConstraintList)}\n")
  t << "{"; // ends at endConstraintList
  t << "{";
  startBold();
  newParagraph();
  docify(header);
  endBold();
  t << "}";
  newParagraph();
  incrementIndentLevel();
  t << rtf_Style_Reset << rtf_DList_DepthStyle();
}

void RTFGenerator::startConstraintParam()
{
  DBG_RTF(t << "{\\comment (startConstraintParam)}\n")
  startEmphasis();
}

void RTFGenerator::endConstraintParam()
{
  DBG_RTF(t << "{\\comment (endConstraintParam)}\n")
  endEmphasis();
  t << " : ";
}

void RTFGenerator::startConstraintType()
{
  DBG_RTF(t << "{\\comment (startConstraintType)}\n")
  startEmphasis();
}

void RTFGenerator::endConstraintType()
{
  DBG_RTF(t << "{\\comment (endConstraintType)}\n")
  endEmphasis();
  t << " ";
}

void RTFGenerator::startConstraintDocs()
{
  DBG_RTF(t << "{\\comment (startConstraintDocs)}\n")
}

void RTFGenerator::endConstraintDocs()
{
  DBG_RTF(t << "{\\comment (endConstraintDocs)}\n")
  newParagraph();
}

void RTFGenerator::endConstraintList()
{
  DBG_RTF(t << "{\\comment (endConstraintList)}\n")
  newParagraph();
  decrementIndentLevel();
  m_omitParagraph = TRUE;
  t << "}";
}

void RTFGenerator::startIndexListItem()
{
  DBG_RTF(t << "{\\comment (startIndexListItem)}\n")
}

void RTFGenerator::endIndexListItem()
{
  DBG_RTF(t << "{\\comment (endIndexListItem)}\n")
  t << "\\par\n";
}

void RTFGenerator::startInlineHeader()
{
  DBG_RTF(t << "{\\comment (startInlineHeader)}\n")
  t << "{\n";
  t << rtf_Style_Reset << rtf_Style["Heading5"].reference();
  startBold();
}

void RTFGenerator::endInlineHeader()
{
  DBG_RTF(t << "{\\comment (endInlineHeader)}\n")
  endBold();
  t << "\\par";
  t << "}\n";
}

void RTFGenerator::startMemberDocSimple(bool isEnum)
{
  DBG_RTF(t << "{\\comment (startMemberDocSimple)}\n")
  t << "{\\par\n";
  t << "{" << rtf_Style["Heading5"].reference() << "\n";
  if (isEnum)
  {
    t << theTranslator->trEnumerationValues();
  }
  else
  {
    t << theTranslator->trCompoundMembers();
  }
  t << ":\\par}\n";
  t << rtf_Style_Reset << rtf_DList_DepthStyle();
  t << "\\trowd \\trgaph108\\trleft426\\tblind426"
       "\\trbrdrt\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrl\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrb\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrr\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrh\\brdrs\\brdrw10\\brdrcf15 "
       "\\trbrdrv\\brdrs\\brdrw10\\brdrcf15 \n";
  int i,n=3,columnPos[3] = { 25, 50, 100 };
  if (isEnum)
  {
    columnPos[0]=30;
    columnPos[1]=100;
    n=2;
  }
  for (i=0;i<n;i++)
  {
    t << "\\clvertalt\\clbrdrt\\brdrs\\brdrw10\\brdrcf15 "
         "\\clbrdrl\\brdrs\\brdrw10\\brdrcf15 "
         "\\clbrdrb\\brdrs\\brdrw10\\brdrcf15 "
         "\\clbrdrr \\brdrs\\brdrw10\\brdrcf15 "
         "\\cltxlrtb "
         "\\cellx" << (rtf_pageWidth*columnPos[i]/100) << "\n";
  }
  t << "\\pard \\widctlpar\\intbl\\adjustright\n";
}

void RTFGenerator::endMemberDocSimple(bool)
{
  DBG_RTF(t << "{\\comment (endMemberDocSimple)}\n")
  t << "}\n";
}

void RTFGenerator::startInlineMemberType()
{
  DBG_RTF(t << "{\\comment (startInlineMemberType)}\n")
  t << "{\\qr ";
}

void RTFGenerator::endInlineMemberType()
{
  DBG_RTF(t << "{\\comment (endInlineMemberType)}\n")
  t << "\\cell }";
}

void RTFGenerator::startInlineMemberName()
{
  DBG_RTF(t << "{\\comment (startInlineMemberName)}\n")
  t << "{";
}

void RTFGenerator::endInlineMemberName()
{
  DBG_RTF(t << "{\\comment (endInlineMemberName)}\n")
  t << "\\cell }";
}

void RTFGenerator::startInlineMemberDoc()
{
  DBG_RTF(t << "{\\comment (startInlineMemberDoc)}\n")
  t << "{";
}

void RTFGenerator::endInlineMemberDoc()
{
  DBG_RTF(t << "{\\comment (endInlineMemberDoc)}\n")
  t << "\\cell }{\\row }\n";
}

void RTFGenerator::writeLineNumber(const char *ref,const char *fileName,const char *anchor,int l)
{
  bool rtfHyperlinks = Config_getBool(RTF_HYPERLINKS);

  m_doxyCodeLineOpen = true;
  QCString lineNumber;
  lineNumber.sprintf("%05d",l);
  if (m_prettyCode)
  {
    if (fileName && !m_sourceFileName.isEmpty() && rtfHyperlinks)
    {
      QCString lineAnchor;
      lineAnchor.sprintf("_l%05d",l);
      lineAnchor.prepend(stripExtensionGeneral(m_sourceFileName, ".rtf"));
      t << "{\\bkmkstart ";
      t << rtfFormatBmkStr(lineAnchor);
      t << "}";
      t << "{\\bkmkend ";
      t << rtfFormatBmkStr(lineAnchor);
      t << "}\n";
    }
    t << lineNumber << " ";
  }
  else
  {
    t << l << " ";
  }
  m_col=0;
}
void RTFGenerator::startCodeLine(bool)
{
  m_doxyCodeLineOpen = true;
  m_col=0;
}
void RTFGenerator::endCodeLine()
{
  if (m_doxyCodeLineOpen) lineBreak();
  m_doxyCodeLineOpen = false;
}

void RTFGenerator::startLabels()
{
}

void RTFGenerator::writeLabel(const char *l,bool isLast)
{
  t << "{\\f2 [" << l << "]}";
  if (!isLast) t << ", ";
}

void RTFGenerator::endLabels()
{
}

void RTFGenerator::startFontClass(const char *name)
{
  int cod = 2;
  QCString qname(name);
  if (qname == "keyword")            cod = 17;
  else if (qname == "keywordtype")   cod = 18;
  else if (qname == "keywordflow")   cod = 19;
  else if (qname == "comment")       cod = 20;
  else if (qname == "preprocessor")  cod = 21;
  else if (qname == "stringliteral") cod = 22;
  else if (qname == "charliteral")   cod = 23;
  else if (qname == "vhdldigit")     cod = 24;
  else if (qname == "vhdlchar")      cod = 25;
  else if (qname == "vhdlkeyword")   cod = 26;
  else if (qname == "vhdllogic")     cod = 27;
  t << "{\\cf" << cod << " ";
}

void RTFGenerator::endFontClass()
{
  t << "}";
}
